package junior.db.mapping;

import com.google.common.collect.Maps;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ReflectionUtils;

import javax.persistence.Column;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class AnnotationColumnMapping implements ColumnMapping {
	private Map<String, Map<String, String>> tableFieldMapping = new HashMap<String, Map<String, String>>();
	private Map<String, Map<String, String>> tableColumnMapping = new HashMap<String, Map<String, String>>();
	
	private ColumnMapping columnMapper;
	private TableMapping tableMapper;
	
	public AnnotationColumnMapping(TableMapping tableMapper, ColumnMapping columnMapper) {
		this.tableMapper = tableMapper;
		this.columnMapper = columnMapper;
	}
	
	@Override
	public String field2Column(Class<?> entityClass, String fieldName) {
		if (entityClass != null && FIELD_REGEX.matcher(fieldName).matches()) {
			String table = tableMapper.entity2TableName(entityClass);

			Map<String, String> fieldMapping = tableFieldMapping.get(table);
			if (fieldMapping == null) {
				fieldMapping = Maps.newHashMap();
				tableFieldMapping.put(table, fieldMapping);
			}
			Map<String, String> columnMapping = tableColumnMapping.get(table);
			if (columnMapping == null) {
				columnMapping = Maps.newHashMap();
				tableColumnMapping.put(table, columnMapping);
			}
			
			String columnName = fieldMapping.get(fieldName);
			if (columnName == null) {
				Column column = null;
				
				Field field = ReflectionUtils.findField(entityClass, fieldName);
				if (field != null) {
					column = field.getAnnotation(Column.class);	
				}
                if (column == null) {
                    PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(entityClass, fieldName);
                    if (pd != null) {
                        Method getter = pd.getReadMethod();
    
                        if (pd.getWriteMethod() != null && getter != null) {
                            column = getter.getAnnotation(Column.class);
                        }
                    }
                }
				
				if (column != null) {
					columnMapping.put(column.name(), fieldName);
					columnName = column.name();
				} else {
					columnName = columnMapper.field2Column(entityClass, fieldName);
				}
				
				fieldMapping.put(fieldName, columnName);
			}
			
			return columnName;
		}
		
		return columnMapper.field2Column(entityClass, fieldName);
	}

	@Override
	public String column2Field(String tableName, String columnName) {
		Map<String, String> columnMapping = tableColumnMapping.get(tableName);
		if (columnMapping != null) {
			String field = columnMapping.get(columnName);
			if (field != null) return field;
		}
		return columnMapper.column2Field(tableName, columnName);
	}

	public ColumnMapping getColumnMapper() {
		return columnMapper;
	}
	public TableMapping getTableMapper() {
		return tableMapper;
	}

}
